#include "camera.h"
#include "scene.h"
#include "serialscene.h"
#include "serialcamera.h"
#include "futurecamera.h"
#include "futurescene.h"
#include "sphere.h"
#include "triangle.h"
#include "mattematerial.h"
#include "glossymaterial.h"
#include "glassymaterial.h"
#include "futureglassymaterial.h"
#include "mirrormaterial.h"
#include "directluminaire.h"
#include "color.h"
#include "vector.h"
#include "point.h"
#include "trajectory.h"
#include "orientation.h"
#include "trackingorientation.h"
#include "circulartrajectory.h"
#include "stopwatch.h"
#include "tools.h"

using namespace std;

int main( int argc, char** argv )
{
    //DB_REP_MSG( "Beninning Execution" );
    
    StopWatch ticker;
    
    //ASSERT_MSG( argc == 6, "Insufficient parameters: $sowilo W H PMODE[S|P|F] WRITEMODE[Y|N]" );
    
    int w, h, n;
    char pmode, wmode;
    str2num( argv[1], w );
    str2num( argv[2], h );
    str2num( argv[3], n );
    pmode = argv[4][0];
    wmode = argv[5][0];

    // Generic Orientation
    Orientation ornt;

    double scn_nRefr = 1.0;
    Color  scn_ambi = CLR_WHITE;
    Color  scn_background = CLR_GRAY50;
    int    scn_depthLim = 20;
    Scene* scn;
    switch( pmode )
    {
        case 'S' : scn = new SerialScene( scn_nRefr, scn_ambi, scn_background, scn_depthLim ); break;
        case 'P' : scn = new Scene( scn_nRefr, scn_ambi, scn_background, scn_depthLim ); break;
        case 'F' : scn = new FutureScene( scn_nRefr, scn_ambi, scn_background, scn_depthLim ); break;
    }

    vector<Primitive*> prims;
    vector<Trajectory*> trajs;
    vector<Material*> mats;

    Material* mat;
    Trajectory* traj;
    Primitive* prim;
    for( int i=0; i<n; i++ )
    {
        if( i%2 )
            if( pmode == 'F' )
                mat = new FutureGlassyMaterial( 0.8, 2.1, 0.05, CLR_WHITE );
            else
                mat = new GlassyMaterial( 0.8, 2.1, 0.05, CLR_WHITE );
        else
            mat  = new MirrorMaterial( 0.9, 0.1, CLR_BLUE );
        double v = .1;
        traj = new CircularTrajectory( Vector( 0, 0, 1 ), 5, v, Point( 0, 0, -10 ), i / ( n * v ) );
        prim = new Sphere( 0.25, mat, traj, &ornt );
        prims.push_back( prim );
        mats.push_back( mat );
        trajs.push_back( traj );
        scn->addPrimitive( prim );
    }

    double v = .2;
    mat = new GlossyMaterial( 0.9, 2, 0.7, 0.1, CLR_GREEN );
    traj = new CircularTrajectory( Vector( 0, 1, 0 ), 20, v, Point( -20, 0, -10 ), 0 );
    prim = new Sphere( 3.0, mat, traj, &ornt );
    prims.push_back( prim );
    mats.push_back( mat );
    trajs.push_back( traj );

    scn->addPrimitive( prim );

    mat  = new MatteMaterial( 0.8, 0.1, CLR_RED );
    traj = new CircularTrajectory( Vector( 0, 1, 0 ), 20, -v, Point( 20, 0, -10 ), 1 / ( 2 * v ) );
    prim = new Sphere( 3.0, mat, traj, &ornt );

    prims.push_back( prim );
    mats.push_back(  mat  );
    trajs.push_back( traj );

    scn->addPrimitive( prim );

    DirectLuminaire    lum( Vector( 1.0, -3.0, -1.0 ).u(), 0.8 );
    scn->addLuminaire( &lum );

    Trajectory          targTraj( Point( 0, 0, -10 ), 0, 0 );
    Trajectory          camTraj ( Point( 0, 10, 5 ), 0, 0 );
    TrackingOrientation camOrnt( &camTraj, &targTraj );

    Point p0 = Vector( 0, 1, 0 );
    double fovy = 45.0;
    Camera* cam;
    switch( pmode )
    {
        case 'S' : cam = new SerialCamera( scn, &camTraj, &camOrnt, p0, w, h, fovy ); break;
        case 'P' : cam = new Camera(       scn, &camTraj, &camOrnt, p0, w, h, fovy ); break;
        case 'F' : cam = new FutureCamera( scn, &camTraj, &camOrnt, p0, w, h, fovy ); break;
    }

    cam->initFrames( 0, 10, 12 );
    
    ticker.start();
    cam->videoRender();
    ticker.stop();

    if( wmode == 'Y' )
        cam->writeFrames( "output/op-vid", ".ppm" );

    for( int i=0; i<prims.size(); i++ )
        delete prims[i];
    for( int i=0; i<mats.size(); i++ )
        delete mats[i];
    for( int i=0; i<trajs.size(); i++ )
        delete trajs[i];
        
    cout << ticker.elapsed() << flush;
}
